﻿using PSE.Framework.Communication.Configuration.Adapters.File;
using PSE.Framework.Communication.Adapters;
using System.IO;
using PSE.Framework.Communication.Configuration.Adapters;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Diagnostics;
using FileHelpers;
using PSE.Framework.Communication.Messages;

namespace PSE.Framework.Communication.Adapters.File
{
    public abstract class AsyncFileInputAdapter<TConfig> : AsyncStreamInputAdapter<TConfig, object, FileReceiveParameter>, IAsyncFileInputAdapter
        where TConfig : FileInputAdapterConfiguration
    {
        #region Constantes
        //tempo de espera entre as verificacoes de acesso do arquivo sendo importado
        //aumentar esse numero pode gerar um delay desnecessario na importacao
        //diminuir esse numero pode gerar um aumento descessario de IO na importacao
        protected const int WAIT_FOR_FILE_CREATE_SLEEP = 600;//600ms

        //tempo de espera entre importacaoes de arquivos, importante para manter baixo o processamento utilizado pelo componente
        protected const int IMPORT_LOOP_SLEEP = 10;//10ms

        //numero minimo de linhas a serem processadas antes de executar thread sleep para diminuir a carga de processamento        
        //quanto maior -> mais uso continuo de processador e menor tempo de execução  (menos sleeps)
        //quanto menor -> menos uso continuo de processador e maior tempo de execução (mais sleeps)
        protected const int MIN_ROWS_TO_PROCESS_BEFORE_SLEEP = 100;

        //numero maximo de linhas a serem processadas antes de executar thread sleep para diminuir a carga de processamento
        //quanto maior -> mais uso continuo de processador e menor tempo de execução  (menos sleeps)
        //quanto menor -> menos uso continuo de processador e maior tempo de execução (mais sleeps)
        protected const int MAX_ROWS_TO_PROCESS_BEFORE_SLEEP = 500;
        #endregion

        #region Struct de Apoio
        protected struct FileInformation
        {
            public DateTime CreationTime;
            public string FullPath;
        }
        #endregion

        #region Propriedades
		private bool _error = false;

        private Queue<string> _filesToImport;
        /// <summary>
        /// Fila de arquivos a serem importados
        /// </summary>
        protected Queue<string> FilesToImport
        {
            get { return _filesToImport; }
            set { _filesToImport = value; }
        }

        /// <summary>
        /// Objeto de observação de arquivos criados
        /// </summary>
        private FileSystemWatcher _incomingWatcher;

        /// <summary>
        /// Thread de importacao de arquivos
        /// </summary>
        private Thread _fileImportThread;

        /// <summary>
        /// Flag que indica se deve concluir o processamento do arquivo atual e não processar mais nenhum arquivo
        /// </summary>
        private ManualResetEvent _closeProcessResetEvent;

        /// <summary>
        /// Flag que indica se deve cancelar o processamento do arquivo atual e passar para o próximo arquivo
        /// </summary>
        private ManualResetEvent _cancelStreamResetEvent;
        /// <summary>
        /// Flag utilizado para garantir que o método CancelStream só continue quando o cancelamento for concluído
        /// </summary>
        private ManualResetEvent _continueCancelStreamResetEvent;
        /// <summary>
        /// Flag que indica se uma importação está em andamento e se o cancelamento pode ser realizado
        /// </summary>
        private ManualResetEvent _canCancelStreamResetEvent;

        #endregion

		#region Eventos
		public event EventHandler<File.FileCreatedEventArgs> NewFileCreated;
		#endregion

		#region Construtor
		public AsyncFileInputAdapter()
        {
        }
        #endregion

        #region Metodos relacionados a monitoramento da pasta de entrada
        /// <summary>
        /// Iniciar o processo de observacao do diretorio de entrada
        /// </summary>
        protected void StartInputPathWatcher()
        {
            string filter = this.Configuration.FileNameFilter;

            //recuperar os arquivos contidos no caminho de entrada e adicioná-los na fila de importação
            RetrieveAndEnqueueFilesFromInputPath(filter);

            _incomingWatcher = new FileSystemWatcher();
            _incomingWatcher.Path = base.Configuration.InputPath;
            _incomingWatcher.IncludeSubdirectories = true;
            _incomingWatcher.Filter = filter;
            _incomingWatcher.Created += new FileSystemEventHandler(OnFileCreated);
            _incomingWatcher.EnableRaisingEvents = true;
        }

        /// <summary>
        /// Parar o processo de observacao do diretorio de entrada
        /// </summary>
        protected void StopInputPathWatcher()
        {
            _incomingWatcher.EnableRaisingEvents = false;
            _incomingWatcher.Changed -= new FileSystemEventHandler(OnFileCreated);
            _incomingWatcher.Dispose();

            lock (FilesToImport)
            {
                FilesToImport.Clear();
            }
        }

        /// Handler do evento disparado na criação de novos arquivos no diretório de input
        /// <param name="source"></param>
        /// <param name="e"></param>        
        protected virtual void OnFileCreated(object source, FileSystemEventArgs e)
        {
            //adicionar o arquivo a fila de importacao
            lock (FilesToImport)
            {
                //Antes de adicionar o caminho do arquivo na fila de importacao, deve ser verificado se o mesmo ja nao se encontra na fila
                //Isso deve ser feito pois foi identificado que quando um arquivo é salvo no diretorio monitorado (ex: editar um arquivo no notepad e salvar no diretorio)
                //O evento created é disparado 2 vezes, isso ocorre devido a forma como o notepad e outras aplicações realizam o salvamento do arquivo em disco
				if (!FilesToImport.Contains(e.FullPath))
				{
					FilesToImport.Enqueue(e.FullPath);
					if (NewFileCreated != null)
						NewFileCreated(source, new FileCreatedEventArgs(e.FullPath));
				}
            }
        }
        #endregion

        #region Abertura do processo de importacao
        /// <summary>
        /// Recuperar os arquivos no diretorio de entrada e enfileirá-los
        /// </summary>
        private void RetrieveAndEnqueueFilesFromInputPath(string fileNameFilter)
        {
            string inputPath = base.Configuration.InputPath;
            List<FileInformation> fiList = new List<FileInformation>();

            this.FilesToImport = new Queue<string>();

            //recuperar a lista de arquivos do diretório de entrada
            if (Directory.Exists(inputPath))
            {
                string[] files = Directory.GetFiles(inputPath, fileNameFilter, SearchOption.AllDirectories);
                if (files != null && files.Length > 0)
                {
                    FileInformation fi;
                    //recuperar os arquivos e suas informacoes de criacao
                    for (int i = 0; i < files.Length; i++)
                    {
                        fi = new FileInformation();
                        fi.FullPath = files[i];
                        fi.CreationTime = System.IO.File.GetCreationTime(fi.FullPath);
                        fiList.Add(fi);
                    }

                    //ordenar os arquivos de acordo com a data de criacao
                    fiList.Sort(new Comparison<FileInformation>(CompareFileCreationTime));

                    //adicionar os arquivos, do mais antigo para o mais novo na fila
                    for (int i = 0; i < fiList.Count; i++)
                    {
                        lock (this.FilesToImport)
                        {
                            this.FilesToImport.Enqueue(fiList[i].FullPath);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Comparar dois fileinfos de forma decrescente
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        private int CompareFileCreationTime(FileInformation a, FileInformation b)
        {
            return a.CreationTime.CompareTo(b.CreationTime);
        }
        #endregion

        #region Metodo de Importacao de Arquivos
        /// <summary>
        /// Metodo de importacao de arquivos. Este metodo fica em loop retirando arquivos da fila e importando
        /// </summary>
        private void ImportFiles()
        {
            string outputPath = this.Configuration.OutputPath;
            string cancelPath = this.Configuration.CancelPath;
            string errorPath = this.Configuration.ErrorPath;

            //ficar em loop equanto nao for ordenado termino do processo de importacao, ou o cancelamento do stream
            while (!_closeProcessResetEvent.WaitOne(IMPORT_LOOP_SLEEP))
            {
                if (FilesToImport.Count > 0)
                {
                    string fullPath;
                    lock (FilesToImport)
                    {
                        //retirar um arquivo da fila de importacao
                        fullPath = FilesToImport.Dequeue();

                        //caso o arquivo empilhado nao exista mais, passar para o proximo
                        if (!System.IO.File.Exists(fullPath))
                            continue;
                    }

                    // Volta os flags de cancelamento para os estados iniciais para garantir 
                    // que o cancelamento de um arquivo anterior não cancele o atual

                    // _cancelStream = false;
                    _cancelStreamResetEvent.Reset();
                    // _continueCancelStream = false;
                    _continueCancelStreamResetEvent.Reset();

                    // Indica que o arquivo está sendo importado, assim o método CancelStream pode ser executado
                    // _canCancelStream = true;
                    _canCancelStreamResetEvent.Set();

					Exception unhandledException = null;
					bool errorFileNotFound = false;
					_error = false;

                    try
                    {
                        //importar o arquivo
                        ImportFile(ref fullPath);

                        // Indica que o arquivo não está sendo mais importado, se o método CancelStream for
                        // executado, ele será ignorado
                        // _canCancelStream = false;
                        _canCancelStreamResetEvent.Reset();
                    }
                    //caso o arquivo não seja encontrado
                    catch (FileNotFoundException)
                    {
                        //essa exception pode ser disparada em casos onde o arquivo foi salvo no diretorio, mas antes de o mesmo ser aberto para
                        //importacao, foi removido do diretorio

                        // _canCancelStream = false;
                        _canCancelStreamResetEvent.Reset();

						errorFileNotFound = true;
                    }
                    catch (Exception ex)
                    {

                        // Indica que o arquivo não está sendo mais importado, se o método CancelStream for
                        // executado, ele será ignorado

                        // _canCancelStream = false;
                        _canCancelStreamResetEvent.Reset();

                        // Ao ocorrer qualquer erro na importação do arquivo deve mover o arquivo para
                        // a pasta de erro e informar o client via evento
						_error = true;
						unhandledException = ex;
                    }

                    string destFile;

                    // Verifica se ocorreram erros na importação
					if (!_error && !errorFileNotFound)
					{
						// _cancelStream == false
						if (!_cancelStreamResetEvent.WaitOne(0))
						{
							//caso seja configurado um diretorio de output, mover o arquivo importado
							if (!string.IsNullOrEmpty(outputPath))
							{
                                // Se a importação terminar com sucesso, mover o arquivo para o diretorio 
								// de arquivos processados
								destFile = this.MoveFile(fullPath, outputPath);

								// fechar o stream passando o caminho para onde foi movido o arquivo importado
								this.CloseStream(destFile);
							}
							//caso nao seja configurado um diretorio de output, excluir arquivo importado
							else
							{
								this.DeleteFile(fullPath);

								this.CloseStream();
							}
						}
						else
						{
                            // Se a importação for cancelada e não ocorrerem erros durante o cancelamento,
							// mover o arquivo para o diretorio de arquivos cancelados
							destFile = this.MoveFile(fullPath, cancelPath);

							// Disparando evento de fechamento do stream
							this.CancelStream(destFile);
						}
					}
					else if(_error)
					{
						// Move o arquivo para o diretorio de arquivos com erro
						destFile = this.MoveFile(fullPath, errorPath);

						if (this._streamStatus == StreamStatus.Opened)
							this._streamStatus = StreamStatus.Closed;

						// Dispara evento com o erro
						if(Configuration.MessageReceivedErrorBehavior == MessageReceivedErrorBehavior.Abort)
							this.OnMessageReceivedError(new RecordReadErrorEventArgs(destFile, unhandledException));
					}

                    // Se a importação foi cancelada, no final do cancelamento sinaliza 
                    // para o método CancelStream que a sua execução pode continuar.
                    // _cancelStream == true
                    if (_cancelStreamResetEvent.WaitOne(0))
                        // _continueCancelStream = true;
                        _continueCancelStreamResetEvent.Set();
                }
            }
        }

        /// <summary>
        /// Mover um arquivo para o diretório informado
        /// </summary>
        /// <param name="fullFilePath">caminho completo do arquivo</param>
        /// <param name="destinationDir">diretorio de destino</param>
        protected virtual string MoveFile(string fullFilePath, string destinationDir)
        {
            string destFileName, destFullFilePath;
            string fullFileName = Path.GetFileName(fullFilePath);

            string fileName = Path.GetFileNameWithoutExtension(fullFileName);
            string fileExtension = Path.GetExtension(fullFileName);

            DateTime dt = DateTime.Now;

            destFileName = fileName + dt.ToString("_yyyyMMddhhmmssfff") + fileExtension;

            destFullFilePath = destinationDir + @"\" + destFileName;
            Directory.Move(fullFilePath, destFullFilePath);

            return destFullFilePath;
        }

        /// <summary>
        /// Exclui o arquivo informado
        /// </summary>
        /// <param name="fullFilePath">caminho completo do arquivo</param>
        protected virtual void DeleteFile(string fullFilePath)
        {
            System.IO.File.Delete(fullFilePath);
        }

        /// <summary>
        /// Metodo responsável pela importacao do arquivo
        /// </summary>
        /// <param name="fullPath"></param>
        private void ImportFile(ref string fullPath)
        {
            //numero de linhas a serem processadas antes de executar thread sleep
            //esse numero sera equivalente a 10% do numero de linhas do arquivo, respeitando os limites minimo (100) e maximo (10.000)
            int rowsToProcessBeforeSleep;

            //se o processo nao estiver fechado e nao tiver sido cancelado
            // _cancelStream == false
            if (!_cancelStreamResetEvent.WaitOne(0))
            {
                //aguardar ate que o arquivo esteja totalmente criado e pronto para ser utilizado
                //isso deve ser feito devido a forma de funcionamento do componente FileSystemWatcher que
                //dispara o eventos Created no momento em que a entrada do arquivo é criado no diretório e nao quando o
                //arquivo em si e totalmente criada (ex: na copia de um arquivo, o evento e disparado logo que o arquivo e colado na pasta, 
                //entretanto nesse momento, os dados do arquivo ainda nao foram persistidos)
                WaitForFileCreate(fullPath);

                int totalRowCount = this.Configuration.IgnoreFirstRows;

                try
                {
                    // Move o arquivo para a pasta de arquivos sendo importados
                    string importingPath = this.Configuration.ImportingPath + @"\" + Path.GetFileName(fullPath);
                    System.IO.File.Move(fullPath, importingPath);

                    // Se mover com sucesso, modifica fullPath para ser o importingPath
                    fullPath = importingPath;

                    //abrir o arquivo para importacao
                    OpenFile(fullPath);

                    //recuperar o numero de linhas do arquivos
                    int rowCount = GetFileRowCount();
                    //desconta o numero de linhas de header e o numero de linhas de trailer
                    rowCount -= this.Configuration.IgnoreFirstRows + this.Configuration.IgnoreLastRows;

                    //abrir o stream
                    this.OpenStream(fullPath, rowCount >= 0 ? rowCount : 0);

                    if (rowCount > 0)
                    {
                        //Calcular o numero de linhas que deverao ser processadas antes de um sleep
                        rowsToProcessBeforeSleep = CalculateRowToProcessBeforeSleep(rowCount);



                        object rec;

                        // Le as primeiras linhas e ignora
                        for (int i = 0; i < this.Configuration.IgnoreFirstRows; i++)
                        {
                            ReadRecord();
                        }

                        //enquanto nao chegar ao fim do arquivo, e o stream nao for cancelado
                        // _cancelStream == false
                        while (!_cancelStreamResetEvent.WaitOne(0))
                        {
                            if (rowCount > 0)
                            {
                                // Incrementa o contador do total de linhas do arquivo
                                totalRowCount++;
                                // Decrementa o contador do total de linhas a serem importadas
                                rowCount--;

                                //recuperar um registro
                                rec = ReadRecord();

                                if (rec != null)
                                {
									try
									{
										//processa a mensagem bruta e entregar para o client
										ProcessAndDeliveryMessage(rec);
									}
									catch (Exception ex)
									{
										if (ex is ConvertException)
										{
											((ConvertException)ex).LineNumber = totalRowCount;
										}
										else if (ex.InnerException is ConvertException)
										{
											((ConvertException)ex.InnerException).LineNumber = totalRowCount;
										}

										if (Configuration.MessageReceivedErrorBehavior == MessageReceivedErrorBehavior.Abort)
										{
											//fechar o arquivo
											CloseFile();

											//repassa a exception para um nivel acima tratar
											throw;
										}
										else
										{
											this.OnMessageReceivedError(new RecordReadErrorEventArgs(fullPath, ex));
											_error = true;
										}
									}

                                    //Solucao utilizada para evitar que a importacao utilize 100% de processamento
                                    if ((rowCount % rowsToProcessBeforeSleep) == 0)
                                        Thread.Sleep(1);
                                }
                                else
                                {
                                    break;
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    //fechar o arquivo
                    CloseFile();
                }
                //caso ocorra algum erro de arquivo nao existente na importacao do arquivo, apenas repassa a exception para
                //o metodo mais acima que ira trata-la
                catch (FileNotFoundException)
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// Aguardar pela criacao total do arquivo
        /// </summary>
        /// <param name="fullPath"></param>
        private bool WaitForFileCreate(string fullPath)
        {
            bool fileInUse = true;

            //tentar abrir o arquivo para leitura
            //caso o arquivo esteja em uso, esperar e tentar ler novamente
            while (fileInUse)
            {
                try
                {
                    System.IO.FileStream f = System.IO.File.OpenRead(fullPath);
                    f.Close();
                    fileInUse = false;
                    break;
                }
                catch (FileNotFoundException)
                {
                    //caso nao encontre o arquivo, ignorar e retornar
                    throw;
                }
                catch (IOException)
                {
                    fileInUse = true;
                    Thread.Sleep(WAIT_FOR_FILE_CREATE_SLEEP);
                }
            }

            return true;
        }

        /// <summary>
        /// Calcular o numero de rows a serem processadas antes de um sleep
        /// </summary>
        /// <param name="rowCount"></param>
        /// <returns></returns>
        private static int CalculateRowToProcessBeforeSleep(int rowCount)
        {
            int rowsToProcessBeforeSleep;
            //se o numero de linhas for menor ou igual a 10 vezes a quantidade minima de rows a serem processadas antes de um sleep
            //processar o arquivo todo de uma unica vez sem thread sleep
            if (rowCount <= (MIN_ROWS_TO_PROCESS_BEFORE_SLEEP * 10))
                rowsToProcessBeforeSleep = rowCount;
            //se o numero de linhas for maior ou igual  a 10 vezes a quantidade maxima de rows a serem processadas antes de um sleep
            //assumir o numero maximo configurado
            else if (rowCount >= MAX_ROWS_TO_PROCESS_BEFORE_SLEEP * 10)
                rowsToProcessBeforeSleep = MAX_ROWS_TO_PROCESS_BEFORE_SLEEP;
            //se o numero de linhas for intermediario, calcular 10% do total para assumir como o numero de linhas a serem processadas antes de um sleep
            else
                rowsToProcessBeforeSleep = System.Convert.ToInt32(rowCount / 10);//10% do tamanho do arquivo

            return rowsToProcessBeforeSleep;
        }

        /// <summary>
        /// Método abstrato que retorna o número de linhas de um arquivo
        /// </summary>
        /// <returns></returns>
        protected abstract int GetFileRowCount();

        /// <summary>
        /// Método abstrato responsável pela abertura do arquivo a ser importado
        /// </summary>
        /// <param name="fullPath"></param>
        protected abstract void OpenFile(string fullPath);
        /// <summary>
        /// Método abstrato responsável pela leitura de um registro do arquivo a ser importado
        /// </summary>
        /// <returns></returns>
        protected abstract object ReadRecord();
        /// <summary>
        /// Método abstrato responsável pelo fechamento do arquivo recem importado
        /// </summary>
        protected abstract void CloseFile();
        #endregion

        #region BeginReceive
        /// <summary>
        /// Inicia o recebimento das mensagens
        /// </summary>
        /// <param name="parameter">Parâmetro para recebimento das mensagens</param>
        public override bool BeginReceive(FileReceiveParameter parameter)
        {
            this.VerifyIsOpened();
            this.VerifyStreamIsCreated();
            this.VerifyCanBeginReceive();

            ///criar os semaforos
            _closeProcessResetEvent = new ManualResetEvent(false);
            _cancelStreamResetEvent = new ManualResetEvent(false);
            _continueCancelStreamResetEvent = new ManualResetEvent(false);
            _canCancelStreamResetEvent = new ManualResetEvent(false);

            if (parameter == null)
                parameter = new FileReceiveParameter();

            base.BeginReceive(parameter);

            // FileNameFilter
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.FileNameFilter))
                this.Configuration.FileNameFilter = this.ReceiveParameter.FileNameFilter;

            // InputPath
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.InputPath))
                this.Configuration.InputPath = this.ReceiveParameter.InputPath;

            if (string.IsNullOrEmpty(this.Configuration.InputPath))
                throw new ArgumentNullException("InputPath");

            // ImportingPath
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.ImportingPath))
                this.Configuration.ImportingPath = this.ReceiveParameter.ImportingPath;

            if (string.IsNullOrEmpty(this.Configuration.ImportingPath))
                throw new ArgumentNullException("ImportingPath");

            // OutputPath - É o único path opcional, se estiver em branco apaga o arquivo importado
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.OutputPath))
                this.Configuration.OutputPath = this.ReceiveParameter.OutputPath;

            // ErrorPath
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.ErrorPath))
                this.Configuration.ErrorPath = this.ReceiveParameter.ErrorPath;

            if (string.IsNullOrEmpty(this.Configuration.ErrorPath))
                throw new ArgumentNullException("ErrorPath");

            // CancelPath
            // ----------------------------------------------------
            if (!string.IsNullOrEmpty(this.ReceiveParameter.CancelPath))
                this.Configuration.CancelPath = this.ReceiveParameter.CancelPath;

            if (string.IsNullOrEmpty(this.Configuration.CancelPath))
                throw new ArgumentNullException("CancelPath");

            // IgnoreFirstRows
            // ----------------------------------------------------
            if (this.ReceiveParameter.IgnoreFirstRows != null)
                this.Configuration.IgnoreFirstRows = (int)this.ReceiveParameter.IgnoreFirstRows;

            // IgnoreLastRows
            // ----------------------------------------------------
            if (this.ReceiveParameter.IgnoreLastRows != null)
                this.Configuration.IgnoreLastRows = (int)this.ReceiveParameter.IgnoreLastRows;

            //iniciar a observacao do diretorio de entrada
            StartInputPathWatcher();

            //iniciar a thread de importacao de arquivos
            if (_fileImportThread == null || !_fileImportThread.IsAlive)
                _fileImportThread = new Thread(new ThreadStart(ImportFiles));

            _fileImportThread.Start();

            

            return this.FilesToImport.Count > 0;
        }
        #endregion

        #region Cancelamento do Stream
        /// <summary>
        /// Cancela o processamento do arquivo atual e passa para o próximo arquivo
        /// </summary>
        public override void CancelStream()
        {
            // Permite o cancelamento somente se a importação já foi iniciada e se o stream foi aberto
            // pelo método OpenStream
            // _canCancelStream == true
            if (_canCancelStreamResetEvent.WaitOne(0) && this.StreamStatus == StreamStatus.Opened)
            {
                // Indica que deve cancelar o processamento do arquivo atual e passar para o próximo arquivo
                // _cancelStream == true
                _cancelStreamResetEvent.Set();

                // Aguarda a conclusão do cancelamento
                // _continueCancel == true
                if(Thread.CurrentThread != _fileImportThread)
					_continueCancelStreamResetEvent.WaitOne();
            }
        }
        #endregion

        #region EndReceive
        public override void EndReceive()
        {
            this.VerifyIsOpened();
            this.VerifyCanEndReceive();

            //parar de observar o diretorio de entrada e limpar a fila de arquivos
            this.StopInputPathWatcher();

            //aguardar o termino da thread de importacao de arquivo
            this.StopFileImport();

            base.EndReceive();
        }
        /// <summary>
        /// Termina a thread de importacao de arquivos
        /// </summary>
        private void StopFileImport()
        {
            //ordenar que a importacao dos arquivos sejam interrompidas, permitindo que o arquivo corrente seja importado até o fim
            _closeProcessResetEvent.Set();
            //aguardar o termino da thread de importacao
            if (_fileImportThread.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
                _fileImportThread.Join();
        }

        #endregion
	}
}
